package com.ksana.orm.executor.impl;

import com.ksana.orm.annotation.Delete;
import com.ksana.orm.annotation.Insert;
import com.ksana.orm.annotation.Select;
import com.ksana.orm.annotation.Update;
import com.ksana.orm.config.Configuration;
import com.ksana.orm.enumerate.MethodEnum;
import com.ksana.orm.executor.Executor;
import com.ksana.util.StringUtil;
import lombok.SneakyThrows;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.ParameterizedType;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

/**
 * @author Fall
 */
public class DefaultExecutor implements Executor {
    private Configuration mapperConfiguration = new Configuration();

    @Override
    public <T> T selectOne(Method statement, Object... parameters) throws Exception {
        List<T> list = select(Class.forName(statement.getReturnType().getName()), MethodEnum.SELECT_ONE,
                getSQL(statement.getAnnotation(Select.class).statement(), statement, parameters));

        if (list.size() > 1) {
            try {
                throw new Exception("查询数量大于一");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        return list.get(0);
    }

    @Override
    public <T> List<T> selectList(Method statement, Object... parameters) throws Exception {
        return select(Class.forName(((ParameterizedType) statement.getGenericReturnType()).getActualTypeArguments()[0].getTypeName()),
                MethodEnum.SELECT_LIST, getSQL(statement.getAnnotation(Select.class).statement(), statement, parameters));
    }

    @Override
    public Integer insert(Method statement, Object... parameters) throws Exception {
        return operate(getSQL(statement.getAnnotation(Insert.class).statement(), statement, parameters));
    }

    @Override
    public Integer update(Method statement, Object... parameters) throws Exception {
        return operate(getSQL(statement.getAnnotation(Update.class).statement(), statement, parameters));
    }

    @Override
    public Integer delete(Method statement, Object... parameters) throws Exception {
        return operate(getSQL(statement.getAnnotation(Delete.class).statement(), statement, parameters));
    }

    /**
     * 所有的删除更新插入统一接入方法
     *
     * @param sql
     * @return
     */
    @SneakyThrows
    private Integer operate(String sql) {
        Connection connection = getConnection();

        PreparedStatement preparedStatement = null;

        try {
            preparedStatement = connection.prepareStatement(sql);
            Integer temp = preparedStatement.executeUpdate();
            return temp;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                preparedStatement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * 所有的查询方法统一接入方法
     *
     * @param returnType
     * @param type
     * @param sql
     * @param <T>
     * @return
     */
    private <T> List<T> select(Class returnType, MethodEnum type, String sql) {
        Connection connection = getConnection();

        PreparedStatement preparedStatement = null;

        try {
            preparedStatement = connection.prepareStatement(sql);
            ResultSet resultSet = preparedStatement.executeQuery();
            Class clazz = returnType;
            List<Object> list = new ArrayList<>();

            if (type.equals(MethodEnum.SELECT_LIST) && !returnType.getSuperclass().equals(Number.class)) {
                // 映射值到实体类中
                while (resultSet.next()) {
                    // 加载实体类
                    Object obj = clazz.getDeclaredConstructor().newInstance();
                    for (Field field : clazz.getDeclaredFields()) {
                        clazz.getMethod(StringUtil.getSetter(field.getName()),
                                field.getType()).invoke(obj,
                                resultSet.getObject(field.getName()));
                    }
                    list.add(obj);
                }
            } else {
                while (resultSet.next()) {
                    // 加载实体类
                    Object obj = null;
                    obj = resultSet.getObject(1);
                    list.add(obj);
                }
            }

            return (List<T>) list;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                preparedStatement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * 获取数据库连接
     *
     * @return
     */
    @SneakyThrows
    private Connection getConnection() {
        Connection connection = mapperConfiguration.build();
        connection.setAutoCommit(false);
        return connection;
    }

    /**
     * 拼接sql语句
     *
     * @return
     */
    private String getSQL(String sql, Method statement, Object... parameters) throws Exception {
        if (sql == null) {
            throw new Exception("查询不到sql语句");
        }

        if (parameters != null) {
            Parameter[] parameterName = statement.getParameters();
            for (int i = 0; i < parameters.length; i++) {
                sql = sql.replace("#{" + parameterName[i].getName() + "}", parameters[i].toString());
            }
        }
        return sql;
    }
}
